#ifndef _XF_SECURITY_SHA224_256_HPP_
#define _XF_SECURITY_SHA224_256_HPP_

#include <ap_int.h>
#include <hls_stream.h>

namespace xf {
namespace security {

// ===================== Bit operations =====================
static inline ap_uint<32> ROTR_u32(unsigned n, ap_uint<32> x) {
#pragma HLS INLINE
    return (x >> n) | (x << (32 - n));
}
static inline ap_uint<32> SHR_u32(unsigned n, ap_uint<32> x) {
#pragma HLS INLINE
    return x >> n;
}
static inline ap_uint<32> CH2(ap_uint<32> x, ap_uint<32> y, ap_uint<32> z) {
#pragma HLS INLINE
    // CH(x,y,z) = z ^ (x & (y ^ z))  (2 levels)
    return z ^ (x & (y ^ z));
}
static inline ap_uint<32> MAJ2(ap_uint<32> x, ap_uint<32> y, ap_uint<32> z) {
#pragma HLS INLINE
    // MAJ(x,y,z) = (x & y) | (z & (x ^ y))  (2 levels)
    return (x & y) | (z & (x ^ y));
}
static inline ap_uint<32> BSIG0(ap_uint<32> x) {
#pragma HLS INLINE
    return ROTR_u32(2, x) ^ ROTR_u32(13, x) ^ ROTR_u32(22, x);
}
static inline ap_uint<32> BSIG1(ap_uint<32> x) {
#pragma HLS INLINE
    return ROTR_u32(6, x) ^ ROTR_u32(11, x) ^ ROTR_u32(25, x);
}
static inline ap_uint<32> SSIG0(ap_uint<32> x) {
#pragma HLS INLINE
    return ROTR_u32(7, x) ^ ROTR_u32(18, x) ^ SHR_u32(3, x);
}
static inline ap_uint<32> SSIG1(ap_uint<32> x) {
#pragma HLS INLINE
    return ROTR_u32(17, x) ^ ROTR_u32(19, x) ^ SHR_u32(10, x);
}

// ===================== Internal block =====================
struct SHA256Block { ap_uint<32> M[16]; };

// --------------------- Pre-processing (32b) --------------------
static void preProcessing(hls::stream<ap_uint<32> >& msg_strm,
                          hls::stream<ap_uint<64> >& len_strm,
                          hls::stream<bool>& end_len_strm,
                          hls::stream<SHA256Block>& blk_strm,
                          hls::stream<ap_uint<64> >& nblk_strm,
                          hls::stream<bool>& end_nblk_strm) {
#pragma HLS INLINE off
#pragma HLS interface ap_ctrl_none port=return
    // Process one message per 'end_len_strm' false token
    for (bool done = end_len_strm.read(); !done; done = end_len_strm.read()) {
        ap_uint<64> lenB = len_strm.read();
        ap_uint<64> bitLen = lenB * 8;
        ap_uint<64> nblk = (lenB >> 6) + 1 + ((lenB & 0x3f) > 55);
        end_nblk_strm.write(false);
        nblk_strm.write(nblk);

        // full 512b blocks
        for (ap_uint<64> j = 0; j < (lenB >> 6); ++j) {
#pragma HLS LOOP_TRIPCOUNT min=0 max=1024
            SHA256Block b;
#pragma HLS ARRAY_PARTITION variable=b.M complete
            for (int i = 0; i < 16; ++i) {
#pragma HLS PIPELINE II=1
                ap_uint<32> w = msg_strm.read();
                // byte swap (input is little-endian words in testbench)
                ap_uint<32> bw = ((w & 0x000000ffU) << 24) | ((w & 0x0000ff00U) << 8) |
                                 ((w & 0x00ff0000U) >> 8)  | ((w >> 24) & 0xffU);
                b.M[i] = bw;
            }
            blk_strm.write(b);
        }

        // tail
        ap_uint<6> left = (ap_uint<6>)(lenB & 0x3f);
        if (left == 0) {
            SHA256Block b;
#pragma HLS ARRAY_PARTITION variable=b.M complete
            b.M[0] = 0x80000000U;
            for (int i = 1; i < 14; ++i) { b.M[i] = 0; }
            b.M[14] = (ap_uint<32>)(bitLen >> 32);
            b.M[15] = (ap_uint<32>)bitLen;
            blk_strm.write(b);
        } else if (left < 56) {
            // one padded block
            SHA256Block b;
#pragma HLS ARRAY_PARTITION variable=b.M complete
            for (int i = 0; i < 14; ++i) {
#pragma HLS PIPELINE II=1
                if (i < (left >> 2)) {
                    ap_uint<32> w = msg_strm.read();
                    ap_uint<32> bw = ((w & 0x000000ffU) << 24) | ((w & 0x0000ff00U) << 8) |
                                     ((w & 0x00ff0000U) >> 8)  | ((w >> 24) & 0xffU);
                    b.M[i] = bw;
                } else if (i > (left >> 2)) {
                    b.M[i] = 0;
                } else {
                    ap_uint<2> e = left & 3;
                    if      (e == 0) b.M[i] = 0x80000000U;
                    else if (e == 1) { ap_uint<32> w = msg_strm.read(); b.M[i] = ((w & 0xffU) << 24)     | 0x00800000U; }
                    else if (e == 2) { ap_uint<32> w = msg_strm.read(); b.M[i] = ((w & 0xffffU) << 16)   | 0x00008000U; }
                    else             { ap_uint<32> w = msg_strm.read(); b.M[i] = ((w & 0xffffffU) << 8) | 0x00000080U; }
                }
            }
            b.M[14] = (ap_uint<32>)(bitLen >> 32);
            b.M[15] = (ap_uint<32>)bitLen;
            blk_strm.write(b);
        } else {
            // two padded blocks
            SHA256Block b;
#pragma HLS ARRAY_PARTITION variable=b.M complete
            for (int i = 0; i < 16; ++i) {
#pragma HLS PIPELINE II=1
                if (i < (left >> 2)) {
                    ap_uint<32> w = msg_strm.read();
                    ap_uint<32> bw = ((w & 0x000000ffU) << 24) | ((w & 0x0000ff00U) << 8) |
                                     ((w & 0x00ff0000U) >> 8)  | ((w >> 24) & 0xffU);
                    b.M[i] = bw;
                } else if (i > (left >> 2)) {
                    b.M[i] = 0;
                } else {
                    ap_uint<2> e = left & 3;
                    if      (e == 0) b.M[i] = 0x80000000U;
                    else if (e == 1) { ap_uint<32> w = msg_strm.read(); b.M[i] = ((w & 0xffU) << 24)     | 0x00800000U; }
                    else if (e == 2) { ap_uint<32> w = msg_strm.read(); b.M[i] = ((w & 0xffffU) << 16)   | 0x00008000U; }
                    else             { ap_uint<32> w = msg_strm.read(); b.M[i] = ((w & 0xffffffU) << 8) | 0x00000080U; }
                }
            }
            blk_strm.write(b);
            SHA256Block b1;
#pragma HLS ARRAY_PARTITION variable=b1.M complete
            for (int i = 0; i < 14; ++i) { b1.M[i] = 0; }
            b1.M[14] = (ap_uint<32>)(bitLen >> 32);
            b1.M[15] = (ap_uint<32>)bitLen;
            blk_strm.write(b1);
        }
    }
    end_nblk_strm.write(true);
}

// --------------------- Pre-processing (64b) --------------------
static void preProcessing(hls::stream<ap_uint<64> >& msg_strm,
                          hls::stream<ap_uint<64> >& len_strm,
                          hls::stream<bool>& end_len_strm,
                          hls::stream<SHA256Block>& blk_strm,
                          hls::stream<ap_uint<64> >& nblk_strm,
                          hls::stream<bool>& end_nblk_strm) {
#pragma HLS INLINE off
#pragma HLS interface ap_ctrl_none port=return
    for (bool done = end_len_strm.read(); !done; done = end_len_strm.read()) {
        ap_uint<64> lenB = len_strm.read();
        ap_uint<64> bitLen = lenB * 8;
        ap_uint<64> nblk = (lenB >> 6) + 1 + ((lenB & 0x3f) > 55);
        end_nblk_strm.write(false);
        nblk_strm.write(nblk);

        for (ap_uint<64> j = 0; j < (lenB >> 6); ++j) {
            SHA256Block b;
#pragma HLS ARRAY_PARTITION variable=b.M complete
            for (int i = 0; i < 16; i += 2) {
#pragma HLS PIPELINE II=1
                ap_uint<64> w = msg_strm.read();
                ap_uint<32> l0 = (ap_uint<32>)(w & 0xffffffffULL);
                ap_uint<32> l1 = (ap_uint<32>)((w >> 32) & 0xffffffffULL);
                // byte swap per 32-bit lane
                l0 = ((l0 & 0x000000ffU) << 24) | ((l0 & 0x0000ff00U) << 8) |
                     ((l0 & 0x00ff0000U) >> 8)  | ((l0 >> 24) & 0xffU);
                l1 = ((l1 & 0x000000ffU) << 24) | ((l1 & 0x0000ff00U) << 8) |
                     ((l1 & 0x00ff0000U) >> 8)  | ((l1 >> 24) & 0xffU);
                b.M[i]     = l0;
                b.M[i + 1] = l1;
            }
            blk_strm.write(b);
        }

        ap_uint<6> left = (ap_uint<6>)(lenB & 0x3f);
        if (left == 0) {
            SHA256Block b;
#pragma HLS ARRAY_PARTITION variable=b.M complete
            b.M[0] = 0x80000000U;
            for (int i = 1; i < 14; ++i) { b.M[i] = 0; }
            b.M[14] = (ap_uint<32>)(bitLen >> 32);
            b.M[15] = (ap_uint<32>)bitLen;
            blk_strm.write(b);
        } else {
            SHA256Block b;
#pragma HLS ARRAY_PARTITION variable=b.M complete
            // copy up to 7 lanes of 64b
            for (int i = 0; i < ((left < 56) ? 7 : 8); ++i) {
#pragma HLS PIPELINE II=1
                if (i < (left >> 3)) {
                    ap_uint<64> w = msg_strm.read();
                    ap_uint<32> l0 = (ap_uint<32>)(w & 0xffffffffULL);
                    ap_uint<32> l1 = (ap_uint<32>)((w >> 32) & 0xffffffffULL);
                    l0 = ((l0 & 0x000000ffU) << 24) | ((l0 & 0x0000ff00U) << 8) |
                         ((l0 & 0x00ff0000U) >> 8)  | ((l0 >> 24) & 0xffU);
                    l1 = ((l1 & 0x000000ffU) << 24) | ((l1 & 0x0000ff00U) << 8) |
                         ((l1 & 0x00ff0000U) >> 8)  | ((l1 >> 24) & 0xffU);
                    b.M[i * 2]     = l0;
                    b.M[i * 2 + 1] = l1;
                } else if (i > (left >> 3)) {
                    b.M[i * 2]     = 0;
                    b.M[i * 2 + 1] = 0;
                } else {
                    // boundary
                    if ((left & 4) == 0) {
                        ap_uint<2> e = left & 3;
                        if      (e == 0) { b.M[i*2] = 0x80000000U; b.M[i*2+1] = 0; }
                        else if (e == 1) { ap_uint<64> w = msg_strm.read(); ap_uint<32> l = (ap_uint<32>)(w & 0xffffffffULL);
                                            b.M[i*2] = ((l & 0xffU) << 24) | 0x00800000U; b.M[i*2+1] = 0; }
                        else if (e == 2) { ap_uint<64> w = msg_strm.read(); ap_uint<32> l = (ap_uint<32>)(w & 0xffffffffULL);
                                            b.M[i*2] = ((l & 0xffffU) << 16) | 0x00008000U; b.M[i*2+1] = 0; }
                        else             { ap_uint<64> w = msg_strm.read(); ap_uint<32> l = (ap_uint<32>)(w & 0xffffffffULL);
                                            b.M[i*2] = ((l & 0xffffffU) << 8) | 0x00000080U; b.M[i*2+1] = 0; }
                    } else {
                        ap_uint<64> w = msg_strm.read();
                        ap_uint<32> l0 = (ap_uint<32>)(w & 0xffffffffULL);
                        ap_uint<32> l1 = (ap_uint<32>)((w >> 32) & 0xffffffffULL);
                        l0 = ((l0 & 0xffU) << 24);
                        ap_uint<2> e = left & 3;
                        if      (e == 0) { b.M[i*2] = l0; b.M[i*2+1] = 0x80000000U; }
                        else if (e == 1) { b.M[i*2] = l0; b.M[i*2+1] = ((l1 & 0xffU) << 24)   | 0x00800000U; }
                        else if (e == 2) { b.M[i*2] = l0; b.M[i*2+1] = ((l1 & 0xffffU) << 16) | 0x00008000U; }
                        else             { b.M[i*2] = l0; b.M[i*2+1] = ((l1 & 0xffffffU) << 8) | 0x00000080U; }
                    }
                }
            }
            if (left < 56) {
                b.M[14] = (ap_uint<32>)(bitLen >> 32);
                b.M[15] = (ap_uint<32>)bitLen;
                blk_strm.write(b);
            } else {
                blk_strm.write(b);
                SHA256Block b1;
#pragma HLS ARRAY_PARTITION variable=b1.M complete
                for (int i = 0; i < 14; ++i) { b1.M[i] = 0; }
                b1.M[14] = (ap_uint<32>)(bitLen >> 32);
                b1.M[15] = (ap_uint<32>)bitLen;
                blk_strm.write(b1);
            }
        }
    }
    end_nblk_strm.write(true);
}

// -------------------- Digest (single II=1 loop, no t<16 mux) ---------
template <int H_W>
static void sha256Digest(hls::stream<ap_uint<64> >& nblk_strm,
                         hls::stream<bool>& end_nblk_strm,
                         hls::stream<SHA256Block>& blk_strm,
                         hls::stream<ap_uint<H_W> >& hash_strm,
                         hls::stream<bool>& end_hash_strm) {
#pragma HLS INLINE off
#pragma HLS interface ap_ctrl_none port=return
#pragma HLS EXPRESSION_BALANCE

    // K constants in ROM
    static const ap_uint<32> K[64] = {
        0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U,
        0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U,
        0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU,
        0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U,
        0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U,
        0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U,
        0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U,
        0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xC67178F2U};

#pragma HLS RESOURCE variable=K core=ROM_1P_LUTRAM

    for (bool endf = end_nblk_strm.read(); !endf; endf = end_nblk_strm.read()) {
        ap_uint<64> nblk = nblk_strm.read();

        // IV
        ap_uint<32> H[8];
#pragma HLS ARRAY_PARTITION variable=H complete
        if (H_W == 224) {
            H[0]=0xc1059ed8U; H[1]=0x367cd507U; H[2]=0x3070dd17U; H[3]=0xf70e5939U;
            H[4]=0xffc00b31U; H[5]=0x68581511U; H[6]=0x64f98fa7U; H[7]=0xbefa4fa4U;
        } else {
            H[0]=0x6a09e667U; H[1]=0xbb67ae85U; H[2]=0x3c6ef372U; H[3]=0xa54ff53aU;
            H[4]=0x510e527fU; H[5]=0x9b05688cU; H[6]=0x1f83d9abU; H[7]=0x5be0cd19U;
        }

        // process blocks
        for (ap_uint<64> n = 0; n < nblk; ++n) {
            SHA256Block blk = blk_strm.read();
#pragma HLS ARRAY_PARTITION variable=blk.M complete

            // preload window with M[0..15] to remove t<16 muxes
            ap_uint<32> w0=blk.M[0],  w1=blk.M[1],  w2=blk.M[2],  w3=blk.M[3];
            ap_uint<32> w4=blk.M[4],  w5=blk.M[5],  w6=blk.M[6],  w7=blk.M[7];
            ap_uint<32> w8=blk.M[8],  w9=blk.M[9],  w10=blk.M[10], w11=blk.M[11];
            ap_uint<32> w12=blk.M[12], w13=blk.M[13], w14=blk.M[14], w15=blk.M[15];

            // Working variables
            ap_uint<32> a=H[0], b=H[1], c=H[2], d=H[3], e=H[4], f=H[5], g=H[6], h=H[7];
#pragma HLS register variable=a
#pragma HLS register variable=b
#pragma HLS register variable=c
#pragma HLS register variable=d
#pragma HLS register variable=e
#pragma HLS register variable=f
#pragma HLS register variable=g
#pragma HLS register variable=h

            // ---- Rounds 0..15: Wt = w0, then slide window and append Wt ----
            ROUNDS0_15:
            for (int t = 0; t < 16; ++t) {
#pragma HLS PIPELINE II=1
#pragma HLS EXPRESSION_BALANCE
                ap_uint<32> Wt = w0;

                // compute once per round, keep trees shallow
                ap_uint<32> s1e = BSIG1(e);
                ap_uint<32> s0a = BSIG0(a);
                ap_uint<32> ch  = CH2(e, f, g);
                ap_uint<32> maj = MAJ2(a, b, c);
                ap_uint<32> x1  = h + s1e;
                ap_uint<32> x2  = ch + (Wt + K[t]);
                ap_uint<32> T1  = x1 + x2;
                ap_uint<32> T2  = s0a + maj;
                ap_uint<32> e_n = d + T1;
                ap_uint<32> a_n = T1 + T2;

                h = g; g = f; f = e; e = e_n;
                d = c; c = b; b = a; a = a_n;

                // slide window and append Wt
                w0=w1; w1=w2; w2=w3; w3=w4; w4=w5; w5=w6; w6=w7; w7=w8;
                w8=w9; w9=w10; w10=w11; w11=w12; w12=w13; w13=w14; w14=w15; w15=Wt;
            }

            // ---- Rounds 16..63: scheduled words with the same window ----
            ROUNDS16_63:
            for (int t = 16; t < 64; ++t) {
#pragma HLS PIPELINE II=1
#pragma HLS EXPRESSION_BALANCE
                ap_uint<32> s1 = SSIG1(w14);
                ap_uint<32> s0 = SSIG0(w1);
                ap_uint<32> Wt = (s1 + w9) + (s0 + w0);

                ap_uint<32> s1e = BSIG1(e);
                ap_uint<32> s0a = BSIG0(a);
                ap_uint<32> ch  = CH2(e, f, g);
                ap_uint<32> maj = MAJ2(a, b, c);
                ap_uint<32> x1  = h + s1e;
                ap_uint<32> x2  = ch + (Wt + K[t]);
                ap_uint<32> T1  = x1 + x2;
                ap_uint<32> T2  = s0a + maj;
                ap_uint<32> e_n = d + T1;
                ap_uint<32> a_n = T1 + T2;

                h = g; g = f; f = e; e = e_n;
                d = c; c = b; b = a; a = a_n;

                // slide window
                w0=w1; w1=w2; w2=w3; w3=w4; w4=w5; w5=w6; w6=w7; w7=w8;
                w8=w9; w9=w10; w10=w11; w11=w12; w12=w13; w13=w14; w14=w15; w15=Wt;
            }

            // add back
            H[0] = H[0] + a; H[1] = H[1] + b; H[2] = H[2] + c; H[3] = H[3] + d;
            H[4] = H[4] + e; H[5] = H[5] + f; H[6] = H[6] + g; H[7] = H[7] + h;
        }

        // write digest (big-endian words)
        if (H_W == 224) {
            ap_uint<224> out;
#pragma HLS ARRAY_PARTITION variable=H complete
            for (int i = 0; i < 7; ++i) {
#pragma HLS UNROLL
                ap_uint<32> l = H[i];
                ap_uint<32> be = ((l & 0xffU) << 24) | ((l & 0xff00U) << 8) |
                                 ((l & 0xff0000U) >> 8) | ((l >> 24) & 0xffU);
                out.range(32*i+31, 32*i) = be;
            }
            hash_strm.write(out);
        } else {
            ap_uint<256> out;
#pragma HLS ARRAY_PARTITION variable=H complete
            for (int i = 0; i < 8; ++i) {
#pragma HLS UNROLL
                ap_uint<32> l = H[i];
                ap_uint<32> be = ((l & 0xffU) << 24) | ((l & 0xff00U) << 8) |
                                 ((l & 0xff0000U) >> 8) | ((l >> 24) & 0xffU);
                out.range(32*i+31, 32*i) = be;
            }
            hash_strm.write(out);
        }
        end_hash_strm.write(false);
    }
    end_hash_strm.write(true);
}

// ------------------------------ Top ---------------------------
template <int m_width, int h_width>
static void sha256_top(hls::stream<ap_uint<m_width> >& msg_strm,
                       hls::stream<ap_uint<64> >& len_strm,
                       hls::stream<bool>& end_len_strm,
                       hls::stream<ap_uint<h_width> >& hash_strm,
                       hls::stream<bool>& end_hash_strm) {
#pragma HLS DATAFLOW
    hls::stream<SHA256Block> blk_strm("blk_strm");
#pragma HLS STREAM       variable=blk_strm depth=64
#pragma HLS BIND_STORAGE variable=blk_strm type=fifo impl=lutram

    hls::stream<ap_uint<64> > nblk_strm("nblk_strm");
#pragma HLS STREAM       variable=nblk_strm depth=16
#pragma HLS BIND_STORAGE variable=nblk_strm type=fifo impl=srl

    hls::stream<bool> end_nblk_strm("end_nblk_strm");
#pragma HLS STREAM       variable=end_nblk_strm depth=8
#pragma HLS BIND_STORAGE variable=end_nblk_strm type=fifo impl=srl

    if (m_width == 32) {
        preProcessing((hls::stream<ap_uint<32> >&)msg_strm, len_strm, end_len_strm, blk_strm, nblk_strm, end_nblk_strm);
    } else {
        preProcessing((hls::stream<ap_uint<64> >&)msg_strm, len_strm, end_len_strm, blk_strm, nblk_strm, end_nblk_strm);
    }
    sha256Digest<h_width>(nblk_strm, end_nblk_strm, blk_strm, hash_strm, end_hash_strm);
}

// ------------------------- Public wrappers --------------------
template <int m_width>
static void sha224(hls::stream<ap_uint<m_width> >& msg_strm,
                   hls::stream<ap_uint<64> >& len_strm,
                   hls::stream<bool>& end_len_strm,
                   hls::stream<ap_uint<224> >& hash_strm,
                   hls::stream<bool>& end_hash_strm) {
    sha256_top<m_width, 224>(msg_strm, len_strm, end_len_strm, hash_strm, end_hash_strm);
}

template <int m_width>
static void sha256(hls::stream<ap_uint<m_width> >& msg_strm,
                   hls::stream<ap_uint<64> >& len_strm,
                   hls::stream<bool>& end_len_strm,
                   hls::stream<ap_uint<256> >& hash_strm,
                   hls::stream<bool>& end_hash_strm) {
    sha256_top<m_width, 256>(msg_strm, len_strm, end_len_strm, hash_strm, end_hash_strm);
}

// ---- Wrapper classes for HMAC generic template (F::hash) ----
template <int iW, int ilW, int oW> struct sha2_wrapper;

template <int iW, int ilW>
struct sha2_wrapper<iW, ilW, 256> {
    static void hash(hls::stream<ap_uint<iW> >& msg_strm,
                     hls::stream<ap_uint<ilW> >& len_strm,
                     hls::stream<bool>& end_len_strm,
                     hls::stream<ap_uint<256> >& hash_strm,
                     hls::stream<bool>& end_hash_strm) {
        sha256<iW>(msg_strm, len_strm, end_len_strm, hash_strm, end_hash_strm);
    }
};

template <int iW, int ilW>
struct sha2_wrapper<iW, ilW, 224> {
    static void hash(hls::stream<ap_uint<iW> >& msg_strm,
                     hls::stream<ap_uint<ilW> >& len_strm,
                     hls::stream<bool>& end_len_strm,
                     hls::stream<ap_uint<224> >& hash_strm,
                     hls::stream<bool>& end_hash_strm) {
        sha224<iW>(msg_strm, len_strm, end_len_strm, hash_strm, end_hash_strm);
    }
};

} // namespace security
} // namespace xf

#endif // _XF_SECURITY_SHA224_256_HPP_
